package org.liaohailong.library.widget.header;

import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * Author: Victor
 * Date: 2019/5/17 9:13
 * Description: 头部+内容的布局，头部可被上滑隐藏，让内容部分全屏展示
 * 内部有且仅有一个View {@link HeaderContentLayout}，并且该View中有且仅有两个View，第一个为HeaderView，第二个为ContentView
 * 判断内容部分是否有可滑动的View：
 * 1，上滑时，先将HeaderView上移动隐藏，再滑动ContentView中的可滑动View。
 * 2，下滑时，先判断ContentView是否能消费下滑任务，再交给自身下滑显示HeaderView
 */
public class HeaderScrollView extends ViewGroup {
    private final int mScaledTouchSlop;
    private int mMinimumVelocity;
    private int mMaximumVelocity;
    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;
    private HeaderContentLayout mHeaderContentLayout;

    public HeaderScrollView(Context context) {
        this(context, null);
    }

    public HeaderScrollView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public HeaderScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
//        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mScaledTouchSlop = 5;
        mMinimumVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
        mMaximumVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
        mScroller = new Scroller(context);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() < 1) {
            throw new IllegalArgumentException("did you miss add HeaderContentLayout?");
        }
        View view = getChildAt(0);
        if (view instanceof HeaderContentLayout) {
            mHeaderContentLayout = (HeaderContentLayout) view;
        } else {
            throw new IllegalArgumentException("only HeaderContentLayout can be child view!");
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mHeaderContentLayout.measure(widthMeasureSpec, heightMeasureSpec);
        int totalHeight = mHeaderContentLayout.getTotalHeight();
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        mHeaderContentLayout.layout(0, 0, r - l, b - t);
    }

    private final Point lastPoint = new Point();

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                lastPoint.set((int) ev.getX(), (int) ev.getY());
                if (mVelocityTracker == null) {
                    mVelocityTracker = VelocityTracker.obtain();
                }
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                mVelocityTracker.clear();
                mVelocityTracker.addMovement(ev);
                return false;
            case MotionEvent.ACTION_MOVE:
                boolean shouldInterceptTouchEvent = false;
                int x = (int) ev.getX();
                int y = (int) ev.getY();
                int xOffset = x - lastPoint.x;
                int yOffset = y - lastPoint.y;

                boolean isScrollY = Math.abs(yOffset) > mScaledTouchSlop;
                boolean confirmScrollY = Math.abs(yOffset) > Math.abs(xOffset);
                if (isScrollY && confirmScrollY) {
                    boolean touchInHeaderArea = mHeaderContentLayout.isTouchInHeaderArea(x, y, getScrollY());
                    if (touchInHeaderArea) {
                        // 触点位于header之中，看header是否需要触点事件
                        boolean disallow = mHeaderContentLayout.isHeaderDisallowInterceptTouchEvent();
                        if (disallow) return false;
                    }

                    boolean pullDown = yOffset > 0;
                    //下拉界面时，判断内容部分View是否自己滑动
                    if (pullDown) {
                        float rawX = ev.getRawX();
                        float rawY = ev.getRawY();
                        shouldInterceptTouchEvent = !mHeaderContentLayout.canContentScrollDown(rawX, rawY);
                    }
                    //上拉界面时，判断头部View是否已经全部展示
                    else {
                        int headerHeight = mHeaderContentLayout.getHeaderHeight();
                        int scrollY = getScrollY();
                        shouldInterceptTouchEvent = scrollY < headerHeight;
                    }
                }
                lastPoint.set(x, y);
                Log.i("Victor", "isScrollY =" + isScrollY + " , confirmScrollY=" + confirmScrollY);
                return shouldInterceptTouchEvent;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                return false;
            default:
                break;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                int yOffset = y - lastPoint.y;
                int scrollY = getScrollY();
                scrollY -= yOffset;
                moveTo(scrollY);
                lastPoint.set(x, y);
                mVelocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mVelocityTracker.addMovement(event);
                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                int xVelocity = (int) mVelocityTracker.getXVelocity();
                int yVelocity = (int) mVelocityTracker.getYVelocity();
                if (Math.abs(yVelocity) > mMinimumVelocity) {
                    int startX = getScrollX();
                    int startY = getScrollY();
                    mScroller.fling(startX, startY, xVelocity, -yVelocity, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
                    invalidate();
                }
                break;
            default:
                break;
        }
        return true;
    }

    private void moveTo(int scrollY) {
        int headerHeight = mHeaderContentLayout.getHeaderHeight();
        scrollY = Math.max(0, scrollY);
        scrollY = Math.min(headerHeight, scrollY);
        scrollTo(0, scrollY);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            int currY = mScroller.getCurrY();
            moveTo(currY);
            postInvalidate();
        }
    }
}
